using System;
using System.Collections.Specialized;
using System.Configuration;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Web;
using SecuBat.Crawling;
using SecuBat.Crawling.Forms;
using Css.Diagnostics;
using SecuBat.Logging;

namespace SecuBat.AttackPlugin
{
	/// <summary>
	/// Summary description for Class1.
	/// </summary>
	public class AttackBase : IAttack
	{
		private int _id;
		private int _attackerRunId;
		private int _attackFormId;
		private int _pluginId;
		private NameValueCollection _attackParameters;
		private int _responseCode;
		private String _responseHtml;
		private DateTime _attackTime;
		private TimeSpan _responseDuration;
		private WebForm _attackForm;
		private FormParameter _submitButton;
		private IAnalysis _mainAnalysis;
		private ICommonData _data;
		private String _exploitUrl;

		public AttackBase(int attackerRunId, int webFormId, int pluginId, FormParameter submitButton, IAnalysis analysis, ICommonData data)
		{
			_id = -1;
			_attackerRunId = attackerRunId;
			_attackFormId = webFormId;
			_pluginId = pluginId;
			_attackParameters = new NameValueCollection();
			_responseCode = -1;
			_responseHtml = "";
			_attackTime = DateTime.MinValue;
			_responseDuration = TimeSpan.MinValue;
			_attackForm = null;
			_submitButton = submitButton;
			_mainAnalysis = analysis;
			_data = data;
			_exploitUrl = "";
		}

		#region Public Methods
		public virtual void Execute()
		{
			try
			{
				this.InitializeAttack();
				Thread.Sleep(1);
				this.DoAttack();
				Thread.Sleep(1);
				this.Register();
				Thread.Sleep(1);
				this.AnalyzeAttack();
				Thread.Sleep(1);
			}
			catch (Exception ex)
			{
				LogManager.Write(TraceLevel.Error, ex, "Attack execution");
			}
		}

		public virtual void InitializeAttack()
		{
			// Load form
			if (AttackForm == null)
				AttackForm = CrawlDbManager.GetWebForm(this.AttackFormId);
			if (AttackForm == null)
				throw new ApplicationException("Form to attack failed to load from database!");

			// Parse the html code of the form to get all included form parameters
			AttackForm.ParseForm();

			// Add common headers for more acceptance
//			this.AttackParameters.Add("Accept", "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*");
//			this.AttackParameters.Add("Accept-Language", "en-us");
//			this.AttackParameters.Add("Cache-Control", "no-cache");
//			this.AttackParameters.Add("Accept-Encoding", "gzip, deflate");
			this.AttackParameters.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3328)");
			//this.AttackParameters.Add("Content-Length", "5000");

			if (SubmitButton != null)
			{
				if (SubmitButton.Name.Length > 0)
					this.AttackParameters.Add(SubmitButton.Name, SubmitButton.Value);
			}
		}

		public virtual void DoAttack()
		{
			LogManager.Write(TraceLevel.Information, String.Format("Starting to attack form posting data to web page {0} using method {1}.", AttackForm.ActionAddress.Url, AttackForm.Method), "Attack logging");

			// Attack & get response
			this.AttackTime = DateTime.Now;
			this.AttackUsingParameters(this.AttackParameters);
			this.ResponseDuration = DateTime.Now.Subtract(this.AttackTime);
			this.BuildExploitUrl(this.AttackParameters);
		}

		public virtual void Register()
		{
			Id = AttackDbManager.NewAttack(this.AttackerRunId, this.AttackFormId, this.PluginId, this.AttackParameters, this.ResponseCode, this.ResponseHtml, this.AttackTime, this.ResponseDuration, this.ExploitUrl);
		}

		public virtual void AnalyzeAttack()
		{
			if (MainAnalysis != null)
				MainAnalysis.Execute(this);
		}

		#endregion

		#region Private Methods
		protected void AttackUsingParameters(NameValueCollection parameters)
		{
			try
			{
				string postData = "";
				foreach (String paramKey in parameters.Keys)
				{
					String value = parameters[paramKey];
					if (postData.Length > 0)
						postData += "&";
					postData += paramKey + "=" + HttpUtility.UrlEncode(value);
				}

				HttpWebRequest webRequest = null;

				if (this.AttackForm.Method == FormMethod.Post)
				{
					webRequest = (HttpWebRequest) WebRequest.Create(AttackForm.ActionAddress.Url);
					webRequest.Method = "POST";
					webRequest.ContentType = "application/x-www-form-urlencoded";
					byte[] lbPostBuffer = System.Text.Encoding.GetEncoding(1252).GetBytes(postData);
					webRequest.ContentLength = lbPostBuffer.Length;
					webRequest.Timeout = Int32.Parse(ConfigurationSettings.AppSettings["WebRequestTimeout"]);
					webRequest.Credentials = CredentialCache.DefaultCredentials;
					using (Stream loPostData = webRequest.GetRequestStream())
					{
						loPostData.Write(lbPostBuffer,0,lbPostBuffer.Length);
					}
				}
				else if (this.AttackForm.Method == FormMethod.Get)
				{
					String url = AttackForm.ActionAddress.Url;
					if (url.IndexOf("?") > 0)
						url += "&";
					else
						url += "?";
					url += postData;
					webRequest = (HttpWebRequest) WebRequest.Create(url);
					webRequest.Method = "GET";
					webRequest.Timeout = Int32.Parse(ConfigurationSettings.AppSettings["WebRequestTimeout"]);
					webRequest.Credentials = CredentialCache.DefaultCredentials;
				}
				
				LogManager.Write(TraceLevel.Information, "Executing attack to url " + this.AttackForm.ActionAddress.Url + ".", "Attack logging");
				try
				{
					using (HttpWebResponse loWebResponse = (HttpWebResponse) webRequest.GetResponse())
					{
						Encoding enc = System.Text.Encoding.GetEncoding(1252);
						using (StreamReader loResponseStream = new StreamReader(loWebResponse.GetResponseStream(),enc))
						{
							LogManager.Write(TraceLevel.Information, "Attack Success (Code " + loWebResponse.StatusCode + ") for url " + this.AttackForm.FormIdentifier + ".", "Attacking form");
							this.ResponseHtml = loResponseStream.ReadToEnd();
							this.ResponseCode = (int) loWebResponse.StatusCode;
						}
					}
				}
				catch (WebException ex)
				{
					if (ex.Response != null)
					{
						HttpWebResponse resp = (HttpWebResponse) ex.Response;
						LogManager.Write(TraceLevel.Information, "Attack Success (Code " + resp.StatusCode + ") for url " + this.AttackForm.FormIdentifier + ": " + ex, "Attacking form");
						Encoding enc = System.Text.Encoding.GetEncoding(1252);
						using (StreamReader loResponseStream = new StreamReader(ex.Response.GetResponseStream(), enc))
						{
							this.ResponseHtml = loResponseStream.ReadToEnd();
							this.ResponseCode = (int) resp.StatusCode;
						}
					}
					else
						LogManager.Write(TraceLevel.Error, "Attack Failed for url " + this.AttackForm.FormIdentifier + ": " + ex, "Attacking form");
				}
			}
			catch (Exception ex)
			{
				LogManager.Write(TraceLevel.Error, "Problem: " + this.AttackForm.FormIdentifier + " - " + ex, "Attacking form");
			}
			finally
			{
				if (this.ResponseHtml == null)
					this.ResponseHtml = "";
			}
		}

		protected void BuildExploitUrl(NameValueCollection parameters)
		{
			if (this.AttackForm.Method == FormMethod.Get)
			{
				string postData = "";
				foreach (String paramKey in parameters.Keys)
				{
					String value = parameters[paramKey];
					if (postData.Length > 0)
						postData += "&";
					postData += paramKey + "=" + HttpUtility.UrlEncode(value);
				}

				String url = AttackForm.ActionAddress.Url;
				if (url.IndexOf("?") > 0)
					url += "&";
				else
					url += "?";
				url += postData;
				this.ExploitUrl = url;
			}
		}
		#endregion

		#region Public Properties
		public int Id
		{
			get { return _id; }
			set { _id = value; }
		}

		public int AttackerRunId
		{
			get { return _attackerRunId; }
			set { _attackerRunId = value; }
		}

		public int AttackFormId
		{
			get { return _attackFormId; }
			set { _attackFormId = value; }
		}

		public int PluginId
		{
			get { return _pluginId; }
			set { _pluginId = value; }
		}

		public NameValueCollection AttackParameters
		{
			get { return _attackParameters; }
			set { _attackParameters = value; }
		}

		public int ResponseCode
		{
			get { return _responseCode; }
			set { _responseCode = value; }
		}

		public string ResponseHtml
		{
			get { return _responseHtml; }
			set { _responseHtml = value; }
		}

		public DateTime AttackTime
		{
			get { return _attackTime; }
			set { _attackTime = value; }
		}

		public TimeSpan ResponseDuration
		{
			get { return _responseDuration; }
			set { _responseDuration = value; }
		}

		public WebForm AttackForm
		{
			get { return _attackForm; }
			set { _attackForm = value; }
		}

		public IAnalysis MainAnalysis
		{
			get { return _mainAnalysis; }
			set { _mainAnalysis = value; }
		}

		public FormParameter SubmitButton
		{
			get { return _submitButton; }
			set { _submitButton = value; }
		}

		public ICommonData Data
		{
			get { return _data; }
			set { _data = value; }
		}

		public string ExploitUrl
		{
			get { return _exploitUrl; }
			set { _exploitUrl = value; }
		}

		#endregion
	}
}
